package com.linrui.user.service.impl;

import com.linrui.basic.constants.WxConstants;
import com.linrui.basic.utils.AjaxResult;
import com.linrui.basic.utils.HttpClientUtils;
import com.linrui.basic.utils.MD5Utils;
import com.linrui.user.config.RegisterNoticeRabbitmqConfig;
import com.linrui.user.domain.Logininfo;
import com.linrui.user.domain.User;
import com.linrui.user.domain.Wxuser;
import com.linrui.user.dto.ChangePasswordDto;
import com.linrui.user.dto.RegisterNoticeDto;
import com.linrui.user.dto.UserRegisterDto;
import com.linrui.user.dto.UserbinderWechatDto;
import com.linrui.user.mapper.LogininfoMapper;
import com.linrui.user.mapper.UserMapper;
import com.linrui.user.mapper.WxuserMapper;
import com.linrui.user.service.IUserService;
import com.linrui.basic.service.impl.BaseServiceImpl;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.JSONObject;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.security.util.Password;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author linrui
 * @since 2022-11-14
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;

    @Override
    @Transactional
    public void registerPhone(UserRegisterDto dto) {
        String username = dto.getUsername();
        String mobile = dto.getMobile();
        String password = dto.getPassword();
        String confirmpassword = dto.getConfirmPassword();
        String smsCode = dto.getSmsCode();
        // 1 参数校验
        // 1.1 非空校验
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(mobile) ||StringUtils.isEmpty(smsCode)
        || StringUtils.isEmpty(password) || StringUtils.isEmpty(confirmpassword)){
            throw new RuntimeException("请检查后重新提交");
        }
        // 1.2 校验两次密码是否一致
        if (!confirmpassword.equals(dto.getPassword())) {
            throw new RuntimeException("两次密码输入不一致");
        }
        // 1.3 校验手机验证码输入是否正确 - 从redis中获取当前手机号的注册验证码进行比对
        String smsCodeStr = (String)redisTemplate.opsForValue().get("register_sms_code_"+mobile);
        if (StringUtils.isEmpty(smsCodeStr)){
            throw new RuntimeException("验证码已过期，请重新发送验证码");
        }
        smsCodeStr = smsCodeStr.split(":")[0];
        if (!smsCode.equals(smsCodeStr)){
            throw new RuntimeException("验证码不正确，请重新输入验证码");
        }
        // 1.4 校验用户名是否已存在，如果用户名已存在，提示 不可重复注册
        Logininfo logininfo = logininfoMapper.findUserByMobile(mobile);
        if (Objects.nonNull(logininfo)){
            throw new RuntimeException("该用户已经注册，请勿重复注册");
        }
        // 2 重置logiinfo对象，保存信息到logininfo表
        logininfo = new Logininfo();
        logininfo.setUsername(username);
        logininfo.setPhone(mobile);
        logininfo.setType(1);
        // 0 不可以用 1 可用
        logininfo.setDisable(1);
        // 设置密码
        String salt = UUID.randomUUID().toString();
        logininfo.setSalt(salt);
        password = MD5Utils.encrypByMd5("123" + salt + password);
        logininfo.setPassword(password);
        logininfoMapper.insert(logininfo);
        // 3 保存信息到 用户表
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setPhone(mobile);
        // 1-已注册 2-已激活 0-禁用
        user.setState(2);
        user.setSalt(salt);
        user.setCreatetime(new Date());
        user.setLogininfoId(logininfo.getId());
        userMapper.insert(user);
        // 4 发送注册成功信息给用户 - 短信/邮件 收件人，发送的内容
//        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
//                "inform.sms",
//                new RegisterNoticeDto(mobile,"收到短信通知：你好！你注册的账号已经完成，可以正常使用了！"));  //收件人（phone） 消息内容
//        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
//                "inform.email",
//                new RegisterNoticeDto(mobile,"收到邮件通知：你好！你注册的账号已经完成，可以正常使用了！"));  //收件人（email） 消息内容
//        return AjaxResult.me();
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.sms",new RegisterNoticeDto(mobile,"你好，注册成功，可以正常使用"));
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.sms",new RegisterNoticeDto(mobile,"邮件通知，你已经注册成功，可以正常使用"));

    }

    @Override
    public AjaxResult UserbinderWechat(UserbinderWechatDto dto) {
        // 1 参数非空校验
        String mobile = dto.getMobile();
        String verifyCode = dto.getVerifyCode();
        String accessToken = dto.getAccessToken();
        String openid = dto.getOpenid();

        if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid)) {
            throw new RuntimeException("请填写后重新提交");
        }
        // 2 校验手机验证码是否正确(从redis取出之前的)
        String redisfromsms = (String) redisTemplate.opsForValue().get("bind_sms_code" + mobile);
        if (StringUtils.isEmpty(redisfromsms)) {
            throw new RuntimeException("，请重新发送");
        }
        String sms = redisfromsms.split(":")[0];
        if (verifyCode.equalsIgnoreCase(sms)) {
            throw new RuntimeException("验证码错误");
        }

        // 3 根据手机号判断用户是否已注册
        User user = userMapper.loadByMobile(mobile);
        Logininfo logininfo = null;
        if (Objects.nonNull(user)) {
            // 3.1 如果用户已注册,判断用户是否已绑定微信用户
            Wxuser wxuser = wxuserMapper.findByUserid(user.getId());
            if (Objects.nonNull(wxuser)) {
                // 3.1.1 如果已绑定,就直接抛异常,提示用户 请先解绑
                return AjaxResult.me().setMessage("该手机号已经绑定，请先解绑");
            } else {
                // 3.1.2 如果未绑定,就将用户与微信用户绑定,免密登录
                buildWxuser(accessToken, openid);
                wxuser.setUserId(user.getId());
                // 3.1.2.3 保存wxUser到数据库
                wxuserMapper.insert(wxuser);
                // 3.1.2.4 获取当前用户的logininfo登陆信息,用来做免密登陆
                logininfo = logininfoMapper.findByUserId(user.getId());
            }
        } else {
//              3.2 如果用户未注册,直接添加登录信息 用户信息 微信用户信息,免密登录
            // 3.2.1 封装logininfo对象
            logininfo = buildLogininfo(mobile);
            // 3.2.2 封装并保存user对象
            login2user(logininfo);
            userMapper.insert(user);
            // 3.2.3 获取微信用户对象,并保存
            Wxuser wxuser = buildWxuser(accessToken, openid);
            wxuser.setUserId(user.getId());
            wxuserMapper.insert(wxuser);
        }
        // 4 免密登录
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set("token", token, 24, TimeUnit.HOURS);
        // 5 将token和登录信息都返回给前端
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        logininfo.setPassword("");
        logininfo.setSalt("");
        map.put("logininfo", logininfo);
        return AjaxResult.me().setResult(map);
    }

    @Override
    public AjaxResult changePassword(ChangePasswordDto dto) {
        String mobile = dto.getMobile();
        String smsCode = dto.getSmsCode();
        String password = dto.getPassword();
        String confirmPassword = dto.getConfirmPassword();
        if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(mobile) ||StringUtils.isEmpty(smsCode)
                || StringUtils.isEmpty(password) || StringUtils.isEmpty(confirmPassword)){
            throw new RuntimeException("请检查后重新提交");
        }
        Logininfo logininfo = logininfoMapper.findUserByMobile(mobile);
        if (!Objects.nonNull(logininfo) ){
            throw new RuntimeException("该用户不存在");
        }else {
            if (logininfo.getDisable() ==0) {
                throw new RuntimeException("该账号已被禁用");
            }else {
                //校验手机验证码是否正确(从redis取出之前的)
                String redisfromsms = (String) redisTemplate.opsForValue().get("findPassword_sms_code" + mobile);
                if (StringUtils.isEmpty(redisfromsms)) {
                    throw new RuntimeException("验证码已过期，请重新发送");
                }
                String sms = redisfromsms.split(":")[0];
                if (smsCode.equalsIgnoreCase(sms)) {
                    throw new RuntimeException("验证码错误");
                }
                if (!password.equalsIgnoreCase(confirmPassword)) {
                    throw new RuntimeException("密码两次输入不一致");
                }
                logininfo.setPassword(password);
                User user = userMapper.loadByMobile(mobile);
                String salt = UUID.randomUUID().toString();
                String newPassword = MD5Utils.encrypByMd5("123" + salt + password);
                user.setPassword(newPassword);
                user.setSalt(salt);
                // 4 免密登录
                String token = UUID.randomUUID().toString();
                redisTemplate.opsForValue().set("token", token, 24, TimeUnit.HOURS);
                // 5 将token和登录信息都返回给前端
                Map<String, Object> map = new HashMap<>();
                map.put("token", token);
                logininfo.setPassword("");
                logininfo.setSalt("");
                map.put("logininfo", logininfo);
                return AjaxResult.me().setResult(map);
            }
        }
    }

    private User login2user(Logininfo logininfo) {
        User user = new User();
        BeanUtils.copyProperties(logininfo, user);
        user.setState(2);
        user.setLogininfoId(logininfo.getId());
        user.setCreatetime(new Date());
        return user;
    }

    private Logininfo buildLogininfo(String mobile) {
        Logininfo logininfo = new Logininfo();
        String salt = UUID.randomUUID().toString();
        String password = MD5Utils.encrypByMd5("123" + salt +"123456");
        logininfo.setPassword(password);
        logininfo.setDisable(1);
        logininfo.setType(1);
        logininfo.setUsername(UUID.randomUUID().toString());
        logininfo.setSalt(salt);
        return logininfo;
    }

    private Wxuser buildWxuser(String accessToken, String openid) {
        //替换出微信的获得用户url
        String accessurl = WxConstants.GET_USER_URL
                .replace("accessToken", accessToken)
                .replace("openid", openid);
        //发起请求去微信获取
        String wxuserStr = HttpClientUtils.httpGet(accessurl);
        //转换成对象，并为它的userid赋值
        Wxuser wxuser = wxuser2Object(wxuserStr);
        return wxuser;
    }

    private Wxuser wxuser2Object(String wxuserStr) {
        JSONObject jsonObject = JSONObject.parseObject(wxuserStr);
        if (StringUtils.isEmpty(jsonObject.getString("openid"))){
            throw new RuntimeException("当前微信用户已过期!请重新扫码登录");
        }
        Wxuser wxuser = new Wxuser();
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        String country = jsonObject.getString("country");
        String province = jsonObject.getString("province ");
        String city = jsonObject.getString("city");
        wxuser.setAddress(country+province+city);
        return wxuser;
    }

}
